﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using static UnityEngine.EventSystems.EventTrigger;

namespace ZyGame.Game
{
    internal sealed class EntityManager : Singleton<EntityManager>
    {
        private Dictionary<string, List<Entity>> entitys = new Dictionary<string, List<Entity>>();
        private Dictionary<int, List<EntityComponent>> components = new Dictionary<int, List<EntityComponent>>();

        public Entity CreateEntity(World world, int guid = 0, params EntityComponent[] components)
        {
            string name = world.GetType().Name;
            if (entitys.TryGetValue(name, out List<Entity> entityList) is false)
            {
                entitys.Add(name, entityList = new List<Entity>());
            }

            Entity entity = new Entity(guid == 0 ? Guid.NewGuid().GetHashCode() : guid, world);
            if (components is not null && components.Length is not 0)
            {
                this.components.Add(entity.guid, new List<EntityComponent>(components));
            }

            entityList.Add(entity);
            return entity;
        }

        public bool ReleaseEntity(string world, int guid)
        {
            if (entitys.TryGetValue(world, out List<Entity> entityList) is false)
            {
                return false;
            }

            Entity entity = entityList.Find(x => x.guid == guid);
            if (entity is null)
            {
                return false;
            }

            ClearComponent(entity);
            entityList.Remove(entity);
            return true;
        }

        public Entity GetEntity(string world, int guid)
        {
            if (entitys.TryGetValue(world, out List<Entity> entityList) is false)
            {
                return default;
            }

            return entityList.Find(x => x.guid == guid);
        }

        public Entity[] GetEntities(string world)
        {
            if (entitys.TryGetValue(world, out List<Entity> entityList) is false)
            {
                return default;
            }

            Entity[] entities = new Entity[entitys.Count];
            for (int i = 0; i < entityList.Count; i++)
            {
                entities[i] = entityList[i];
            }

            return entities;
        }

        public void Clear(string world)
        {
            if (!entitys.TryGetValue(world, out List<Entity> list))
            {
                return;
            }

            for (int i = 0; i < list.Count; i++)
            {
                ClearComponent(list[i]);
            }

            list.Clear();
        }

        internal EntityComponent AddComponent(Type type, Entity entity, params object[] dataList)
        {
            if (!components.TryGetValue(entity.guid, out List<EntityComponent> cs))
            {
                components.Add(entity.guid, cs = new List<EntityComponent>());
            }

            EntityComponent component = cs.Find(x => x.GetType() == type);
            if (component is null)
            {
                component = (EntityComponent)Activator.CreateInstance(type);
            }

            cs.Add(component);
            component.entity = entity;
            component.Awake(dataList);
            return component;
        }

        internal EntityComponent GetComponent(Type t, Entity entity)
        {
            if (!components.TryGetValue(entity.guid, out List<EntityComponent> cs))
            {
                return default;
            }

            return cs.Find(x => x.GetType() == t);
        }

        internal EntityComponent[] GetComponents(Entity entity)
        {
            if (!components.TryGetValue(entity.guid, out List<EntityComponent> cs))
            {
                return default;
            }

            return cs.ToArray();
        }

        internal T[] GetComponents<T>(World world) where T : EntityComponent
        {
            List<T> result = new List<T>();
            if (entitys.TryGetValue(world.GetType().Name, out List<Entity> list))
            {
                foreach (var item in list)
                {
                    T m = item.GetComponent<T>();
                    if (m is null)
                    {
                        continue;
                    }

                    result.Add(m);
                }
            }

            return result.ToArray();
        }

        internal bool ReleaseComponent(Entity entity, Type t)
        {
            if (!components.TryGetValue(entity.guid, out List<EntityComponent> cs))
            {
                return false;
            }

            EntityComponent component = cs.Find(x => x.GetType() == t);
            if (component is null)
            {
                return false;
            }

            component.Dispose();
            cs.Remove(component);
            return true;
        }

        internal void ClearComponent(Entity entity)
        {
            if (!components.TryGetValue(entity.guid, out List<EntityComponent> cs))
            {
                return;
            }

            for (int i = 0; i < cs.Count; i++)
            {
                cs[i].Dispose();
            }

            cs.Clear();
        }

        internal bool HasComponent(Entity entity, Type type)
        {
            if (!components.TryGetValue(entity.guid, out List<EntityComponent> cs))
            {
                return false;
            }

            return cs.Find(x => x.GetType() == type) is not null;
        }
    }
}